//////
///// GarryWare Prime
//// Server Player Extension
///

-- DTInt 0: Achieved?
---- -1		: Mystery
---- 0		: Failure
---- 1		: Success

-- DTBool 0: Locked?
---- false	: Unlocked
---- true	: Locked

-- DTBool 3: OnHold?
---- false	: Not on hold
---- true	: On hold


local meta = FindMetaTable( "Player" )
if (not meta) then return end 

-- Only useful when game starts
-- Since we're assuming every player is in the same environment
function meta:GetEnvironment()
	if #ents.FindByClass("func_wareroom") == 0 then
		return ware_env.Get( 1 )
		
	end
	
	for _,v in pairs(ware_env.GetTable()) do
		if v.Players[self] then
			return v
			
		end
		
	end
	
end

function meta:IsWarePlayer()
	return self:Team() == TEAM_HUMANS
	
end

----

function meta:GetAchieved()
	local achieved = self:GetDTInt(0)
	if (achieved == -1) then
		return nil
		
	else
		return (achieved > 0)
		
	end
	
end

function meta:GetLocked()
	return self:GetDTBool(0)
	
end

----

function meta:ApplyWin( )
	self:SetAchievedAndLock( true )
	
end

function meta:ApplyLose( )
	self:SetAchievedAndLock( false )
	
end

function meta:SetAchievedAndLock( bAchieved )
	if WareMaster():PhaseIsPrelude() then return false end
	if not self:IsWarePlayer() then return end
	if self:IsOnHold() then return false end
	if self:GetLocked() then return false end
	
	self:SetAchieved( bAchieved or false, true )
	self:ApplyLock()
	
	return true
end

function meta:SetAchieved( bAchieved )
	if not opt_bAlreadyTested then
		if WareMaster():PhaseIsPrelude() then return false end
		if not self:IsWarePlayer() then return end
		if self:IsOnHold() then return false end
		if self:GetLocked() then return false end
		
	end
	
	self:SetAchievedInternal( (bAchieved and 1) or 0 )
	return true
	
end

----

function meta:IsHold()
	return self:GetDTBool( 3 )
	
end

function meta:ApplyHold()
	if self:GetLocked() then return false end
	return self:SetDTBool( 3, true )
	
end

function meta:RemoveHold()
	return self:SetDTBool( 3, false )
	
end

----

function meta:SetAchievedInternal( iAchieved )
	if self:GetLocked() then return false end
	
	self:SetDTInt( 0, iAchieved )
	return true
	
end

function meta:SetLockedInternal( bLocked )
	self:SetDTBool( 0, bLocked )
	return true
	
end

function meta:SetComboInternal( intCombo )
	self:SetDTInt( 2, intCombo )
	return true
	
end

----

function meta:IsFirst( )
	return self:GetDTBool( 1 )
	
end

function meta:ApplyFirst( )
	self:SetDTBool( 1, true )
	
end

function meta:RemoveFirst( )
	self:SetDTBool( 1, false )
	
end

----

// TODO: Awards
--[[
function meta:GiveAward( sAward )
	if not sAward then return end

	self.m_tokens[sAward] = (self.m_tokens[sAward] or 0) + 1

end

function meta:GiveAwards( tabVar )
	if not tabVar then return end
	
	if #tabVar > 0 then
		for _,id in pairs( tabVar ) do
			self:GiveAward( id )
			
		end
	
	end
	
end
]]--

----

function meta:ShowWinnerEffects()
	local anim = (math.random(0, 1) == 1) and ACT_SIGNAL_FORWARD or ACT_SIGNAL_HALT
	self:DoAnimationEvent( anim ) 
	WareMaster():EmitSound( self, self.tSOUND.OtherWin )
	
	local ed = EffectData()
	ed:SetOrigin( self:GetPos() )
	util.Effect("ware_good", ed, true, true)
	
end


function meta:ShowLoserEffects()
	local anim = (math.random(0, 1) == 1) and ACT_ITEM_THROW or ACT_ITEM_DROP
	self:DoAnimationEvent( anim ) 
	WareMaster():EmitSound( self, self.tSOUND.OtherLose )
	
	local ed = EffectData()
	ed:SetOrigin( self:GetPos() )
	util.Effect("ware_bad", ed, true, true)
	
end

function meta:ApplyLock( dontSendStatusMessage )
	if not self:IsWarePlayer() then return false end
	if WareMaster():PhaseIsPrelude() then return false end
	if self:GetLocked() then return end
	
	if (self:GetAchieved() == nil) then
		self:SetAchievedNoLock( false )
	end
	self:SetLockedInternal( true )
	
	local hasAchieved = self:GetAchieved() or false
	
	if hasAchieved then
		self:AddFrags( 1 )
		self:IncrementCombo()
		
		WareEngine():DeclareFirstWinner(self)
		
		self:ShowWinnerEffects()
	
	else
		self:AddDeaths( 1 )
		self:InterruptCombo()
		
		WareEngine():DeclareFirstLoser(self)
		
		local ed = EffectData()
		ed:SetOrigin( self:GetPos() )
		util.Effect("ware_bad", ed, true, true)
	
	end
	
	-- Usually, don't sens status message
	-- If we're at the end of a minigame (checking all at once after)
	
	// TODO: Global Status Algorithm
	--[[
	if not(dontSendStatusMessage or false) then
		local everyoneStatusIsTheSame = GAMEMODE:CheckGlobalStatus()
		if (everyoneStatusIsTheSame) then
			GAMEMODE:SendEveryoneEvent( hasAchieved )
			return
		else
			--local rp = RecipientFilter()
			--rp:AddPlayer( self )
			umsg.Start("gw_yourstatus", self)
				umsg.Bool(hasAchieved)
				umsg.Bool(false)
			umsg.End()
		end
	end
	]]--
end

function meta:TellDone( )
	--local rp = RecipientFilter()
	--rp:AddPlayer( self )
	umsg.Start("gw_specialstatus", self)
		umsg.Char( 1 )
	umsg.End()
	
end

function meta:ApplyDone( )
	self:SetAchievedNoLock( true )
	self:TellDone()
	
end

----

function meta:GetCombo()
	return self:GetDTInt(3, 0)
end

function meta:SetComboInternal( iBestCombo )
	return self:GetDTInt(3, iBestCombo)
end

function meta:GetBestCombo()
	return self:GetDTInt(4, 0)
end

function meta:SetBestComboInternal( iBestCombo )
	return self:GetDTInt(4, iBestCombo)
end

----

function meta:IncrementCombo()
	local myCombo = self:GetCombo()
	myCombo = myCombo + 1
	self:SetComboInternal( myCombo )
	
	if (myCombo > self:GetBestCombo()) then
		self:SetBestComboInternal( myCombo )
		
	end
	
	--[[
	if (myCombo > GAMEMODE:GetBestStreak()) then
		GAMEMODE:SetBestStreak( myCombo )
		
		// TODO : BEST STREAK BREACK
		umsg.Start("BestStreakEverBreached", nil)
			umsg.Long( GAMEMODE:GetBestStreak() )
		umsg.End()
		
	end
	]]--
	
	return myCombo
end

function meta:InterruptCombo()
	local myOldCombo = self:GetCombo()
	self:SetComboSpecialInteger( 0 )
	
	self:PrintComboMessagesAndEffects( myOldCombo )
	
	return myOldCombo
	
end

function meta:PrintComboMessagesAndEffects( compareCombo )
	// TODO: REDO AN ALGORITHM FOR THAT SHIT
	--[[
	if (compareCombo == GAMEMODE:GetBestStreak()) then
		GAMEMODE:PrintInfoMessage( self:GetName(), " equalized a ", "Server Best Streak of " .. compareCombo .. " Wares!" )
		
		--self:EmitSound( self.tSOUND.TBL_LocalWon[1] , 100 , 125 )
		self:EmitSound( self.tSOUND.GlobalWareningReport , 84 )
		
	elseif (compareCombo >= 3) and (compareCombo == self:GetBestCombo()) then 
		GAMEMODE:PrintInfoMessage( self:GetName(), " scored his ", "Own Best Streak of " .. compareCombo .. " wares!" )
		
		--self:EmitSound( self.tSOUND.TBL_LocalWon[2] , 100 , 119 )
		self:EmitSound( self.tSOUND.GlobalWareningReport, 100, GAMEMODE:GetSpeedPercent())
	
	elseif (compareCombo >= 3) then 
		GAMEMODE:PrintInfoMessage( self:GetName(), " scored a ", "Streak of " .. compareCombo .. " wares." )
		
		self:EmitSound( self.tSOUND.GlobalWareningReport, 100, GAMEMODE:GetSpeedPercent())
	end
	]]--
	WareMaster():EmitSound( self, self.tSOUND.GlobalWareningReport )
	
end

----

function meta:SendHitConfirmation( )
	SendUserMessage( "HitConfirmation", self )
	
end

function meta:EjectWeapons( vectForce, fRandomness )
	for k,weapon in pairs( self:GetWeapons() ) do
		if weapon.WorldModel then
			local weaponSim = ents.Create( "prop_physics" )
			weaponSim:SetPos( self:GetPos() + Vector(0, 0, 32) )
			weaponSim:SetAngles( Angle(math.random(0,360),math.random(0,360),math.random(0,360)) )
			
			weaponSim:SetModel( weapon.WorldModel )
			weaponSim:SetCollisionGroup(COLLISION_GROUP_WORLD)
			weaponSim:Spawn()
			
			local physobj = weaponSim:GetPhysicsObject()
			if physobj:IsValid() then
				physobj:ApplyForceCenter( physobj:GetMass() * (vectForce or Vector(0,0,0) + VectorRand() * fRandomness) )
				physobj:AddAngleVelocity( Angle(math.random(-128,128),math.random(-128,128),math.random(-128,128)) ) 
			end
			
			WareMaster():AppendEntToBin( weaponSim )
			
		end
		
	end
	
	self:StripWeapons( )
	
end

function meta:SimulateDeath( optvectPush, optiObjNumber )
	if self:IsSimDead() then return nil end
	
	self._SimDead = true
	
	self:SetColor(255, 255, 255, 64)
	self:CreateRagdoll( )
	
	if optvectPush then
		umsg.Start("PlayerRagdollEffect")
			umsg.Entity( self )
			umsg.Vector( optvectPush )
			umsg.Char( optiObjNumber or -1 )
		umsg.End()
		
	end
	
	return
end

function meta:IsSimDead()
	return self._SimDead or false
end

function meta:RestoreDeath()
	if not self:IsSimDead() then return false end
	
	self._SimDead = false
	
	self:SetColor(255, 255, 255, 255)
	if self:GetRagdollEntity() then
		self:GetRagdollEntity():Remove()
		
	end
		
	return true
end


// TODO: Legacy code to remove
--[[
-- Imported from GMDM
-- Called by weapons to add recoil
--
function meta:Recoil( pitch, yaw )

	-- On the client it can sometimes process the same usercmd twice
	-- This function returns true if it's the first time we're doing this usercmd
	if ( not SinglePlayer() and not IsFirstTimePredicted() ) then return end

	-- People shouldn't really be playing in SP
	-- But if they are they won't get recoil because the weapons aren't predicted
	-- So the clientside stuff never fires the recoil
	if ( SERVER and SinglePlayer() ) then 
	
		-- Please don't call SendLua in multiplayer games. This uses a lot of bandwidth
		self:SendLua( "LocalPlayer():Recoil("..pitch..","..yaw..")" )
		return 
		
	end
	
	self.LastShoot = 0.5
	self.LastShootSize = math.abs(yaw) + math.abs(pitch)
	
	self.RecoilYaw = self.RecoilYaw or 0
	self.RecoilPitch = self.RecoilPitch or 0
	
	self.RecoilYaw = self.RecoilYaw 		+ yaw
	self.RecoilPitch = self.RecoilPitch 	+ pitch

end

--
-- Shot attacked
--
function meta:DoRecoilThink( pitch, yaw )

	if ( SERVER ) then return end
	if ( self ~= LocalPlayer() ) then return end
	
	local pitch 	= self.RecoilPitch	or 0
	local yaw 		= self.RecoilYaw  	or 0
	
	pitch = pitch
	yaw = yaw
	
	local pitch_d	= math.Approach( pitch, 0.0, 20.0 * FrameTime() * math.abs(pitch) )
	local yaw_d		= math.Approach( yaw, 0.0, 20.0 * FrameTime() * math.abs(yaw) )
	
	self.RecoilPitch 	= pitch_d
	self.RecoilYaw 		= yaw_d
		
	-- Update eye angles
	local eyes = self:EyeAngles()
		eyes.pitch = eyes.pitch + ( pitch - pitch_d )
		eyes.yaw = eyes.yaw + ( yaw - yaw_d )
		eyes.roll = 0
	self:SetEyeAngles( eyes )

end

--
-- Think
--
function meta:Think( )

	-- We spread the recoil out over a few frames to make it less of a shock
	-- This function adds the recoil
	self:DoRecoilThink()

end

function meta:IsObserver()
	return ( self:Team() == TEAM_SPECTATOR and self:GetObserverMode() > OBS_MODE_NONE )
end
]]--
